package com.ilikesoup.instant.monitor.sample.trigger;

import com.ilikesoup.instant.monitor.core.Invocation;
import com.ilikesoup.instant.monitor.core.ChangeEventTrigger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * JavaBean write方法触发器
 * oldValue - 用read方法获取旧值
 *
 */
public class BeanWriteTrigger implements ChangeEventTrigger {

    private static final Map<Method, Method> readMethodMap = new ConcurrentHashMap<>();

    @Override
    public Object copyOldValue(Invocation invocation) {
        Method writeMethod = invocation.getMethod();
        Method readMethod = readMethodMap.get(writeMethod);
        if(readMethod != null) {
            try {
                return invocation.getContext().serialize(readMethod.invoke(invocation.getTarget()));
            } catch (IllegalAccessException | InvocationTargetException e) {
                return CAN_NOT_READ;
            }
        }
        if(writeMethod.getName().length() < 4) {
            return CAN_NOT_READ;
        }
        String readMethodName = getReadMethodName(writeMethod);
        Class<?> clazz = invocation.getTarget().getClass();
        try {
            readMethod = clazz.getMethod(readMethodName);
            if(!readMethod.isAccessible()) {
                readMethod.setAccessible(true);
            }
            readMethodMap.putIfAbsent(writeMethod, readMethod);
            return invocation.getContext().serialize(readMethod.invoke(invocation.getTarget()));
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            return CAN_NOT_READ;
        }
    }

    private String getReadMethodName(Method writeMethod) {
        return "g" + writeMethod.getName().substring(1);
    }

    private String getPropertyName(String writeMethodName) {
        return writeMethodName.substring(3, 4).toLowerCase() + writeMethodName.substring(4);
    }

    @Override
    public void fireChange(Invocation invocation, Object oldValue, PropertyChangeSupport propertyChangeSupport) {
        propertyChangeSupport.firePropertyChange(getPropertyName(invocation.getMethodName()), oldValue, invocation.serialize(invocation.getArgs()[0]));
    }

    public static class BeanWriteListener implements PropertyChangeListener {
        private static final Logger logger = LoggerFactory.getLogger(BeanWriteListener.class);
        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            logger.debug("Bean属性变化监听，对象信息：{}，变更信息：[propName:{}, oldVal:{}, newVal:{}]",
                    evt.getSource(), evt.getPropertyName(), evt.getOldValue(), evt.getNewValue());
        }
    }
}
